/**************************************************************************
 * Copyright (C) 2020-2020  Unisound
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 **************************************************************************
 *
 * Description : user_gpio.c
 * Author      : yuanshifeng@unisound.com
 * Date        : 2020.04.24
 *
 **************************************************************************/
#include "user_gpio.h"
#include "uni_hal_gpio.h"

#define TAG "user_gpio"

static uni_bool g_is_init = false;

int _check_gpio_num_valid(GPIO_NUMBER num)
{
  if (num < 0 || num >= GPIO_NUM_MAX) {
    LOGE(TAG, "error GPIO_NUMBER: %d", num);
    return -1;
  }
  return 0;
}

int user_gpio_init(void) {
  if (!g_is_init) {
    /* TODO: reserve for pinmux tool */
    g_is_init = true;
  }
  return 0;
}

void user_gpio_final(void) {
  if (g_is_init) {
    /* TODO: reserve for pinmux tool */
    g_is_init = false;
  }
}

int user_gpio_set_mode(GPIO_NUMBER num, GPIO_MODE mode) {
  if (mode < GPIO_MODE_IN || mode >= GPIO_MODE_ADC) {
    LOGE(TAG, "error: mode %d need set by use UniPinmuxTool", mode);
    return -1;
  }
  switch (mode) {
    case GPIO_MODE_IN:
      uni_set_gpio_dir(num, 0);
      break;
    case GPIO_MODE_OUT:
      uni_set_gpio_dir(num, 1);
      break;
    default:
      break;
  }
  return 0;
}

int user_gpio_set_pull_mode(GPIO_NUMBER num, GPIO_PULL_MODE mode) {
  if (_check_gpio_num_valid(num)) {
    return -1;
  }
  uni_hal_gpio_set_pull_mode(num, mode);
  return 0;
}

int user_gpio_set_value(GPIO_NUMBER num, int val) {
  if (_check_gpio_num_valid(num)) {
    return -1;
  }
  uni_hal_gpio_set_value(num, val);
  return 0;
}

int user_gpio_get_value(GPIO_NUMBER num) {
  int val = 0;
  if (_check_gpio_num_valid(num)) {
    return -1;
  }
  uni_hal_gpio_get_value(num, &val);

  return val;
}

int user_gpio_interrupt_enable(void) {
  uni_gpio_irq_enable();
  return 0;
}

int user_gpio_interrupt_disable(void) {
  uni_gpio_irq_disable();
  return 0;
}

static _gpio_interrupt_cb g_inter_map[GPIO_NUM_MAX] = {NULL};

static GPIO_NUMBER _get_gpio_number(int num) {
  return (GPIO_NUM_0 + num);
}

static void _gpio_interrupt(int num, int is_high) {
  GPIO_NUMBER gpio = _get_gpio_number(num);
  if (NULL != g_inter_map[gpio]) {
    g_inter_map[gpio](gpio, is_high);
  }
}

int user_gpio_set_interrupt(GPIO_NUMBER num, GPIO_INT_TYPE type,
                                    _gpio_interrupt_cb cb) {
  if (_check_gpio_num_valid(num)) {
    return -1;
  }
  if (type < GPIO_INT_NEG_EDGE || type >= GPIO_INT_MAX) {
    LOGE(TAG, "unknown interrupt type %d", type);
    return -1;
  }
  if (NULL == cb) {
    LOGE(TAG, "callback function for GPIO interrupt cannot be NULL");
    return -1;
  }
  g_inter_map[num] = cb;
  uni_hal_gpio_irq_cfg(num, (int)type, _gpio_interrupt);
  return 0;
}

int user_gpio_clear_interrupt(GPIO_NUMBER num) {
  if (_check_gpio_num_valid(num)) {
    return -1;
  }
  g_inter_map[num] = NULL;
  uni_hal_gpio_irq_clear((int)num);
  return 0;
}


